spaceharderfandomcom-20200214-history
Tutorial 4: Creating a Level
Overview Levels in SpaceRTS are defined in XML files, much in the way that units are. The Level's XML file defines teams, where the player starts, backgrounds, enemies, etc. In this way, a whole level can be designed without touching the code much. In this tutorial, we're going to create a really simple level. Design Principles The core design principle of SpaceRTS is that the game is a tactical RTS where the player selects a small fleet of ships to complete some objective. Levels might have different objectives (ie, defend the space station, escort the convoy, or destroy the enemy base), and the challenge for the player lies in anticipating challenges and selecting the right assortment of units. This sort of design is best exemplified by games like the Ground Control series. Levels begin a briefing screen followed by the actual mission itself (which may begin with a cut scene or some exposition). The briefing screen should give a shory summary of the objective of the mission, with later, more detailed information given in scripted sequences within the level itself. So, before you begin making a level, think about the core objectives of the level, and how best to implement them. Think about how the level fits into the story, and what scripted sequences will be needed to get the point accross. Level Meta-Information Let's start by making a very simple level. In this level, the player will just have to move ships to a waypoint, and then destroy a probe that spawns there. We'll also make a simple cutscene. Create the XML File First, create a file called TestLevel.xml, and put in the following text: Level 1 level1image TRAINING MISSION Your first mission. Complete each training task before moving on to bigger tasks. Select at least one fighter or scout to start the mission. This defines the meta-information needed by the game to make the level playable. All that's required is that the level have a name, some kind of mini-map image (which is displayed in the briefing screen), and a text description. If you want, experiment with replacing the description and map images with assets of your own. Team List Now, add a child field of the level called "teams." This is a list of team objects that registers the teams that will be taking part in the level. Fill it with this text: 1 1 Enforcers 100 255 155 255 2 2 The Hive 255 0 0 255 This defines two teams, the Enforcers, and the Hive. What this does for the level is associate the name "Enforcers" with team 1, and the name "The Hive" with team 2. There is also the notion of an "alliance" -- that is, teams that are allied with one another and won't attack each other. Each team also has a team color for tinting its team-color images. Player Definition Now, we'll need to tell the level about the player. Add the following child field inside the Level node: Enforcers 500 0 enforcerscout enforcerfighter enforcerbomber enforcerfrigate enforcerharvester 5 10 25 50 100 5 This defines what ships the player can use for the level. Each of the strings in "availableShips" represents an XNA asset name. In addition, each of the available ships is given a "weight". The player can only take a number of ships whose weight sums to "maxWeight," and which does not exceed "maxShips." We also see here that the team of the player is given as "Enforcers," which was previously defined in the team list. Scrolling Background In addition to definitions surrounding teams and the player, the level also contains a definition of a scrolling background. This is a component used to draw the parallax background stars, planets, etc. In this level, we're just going to have several layers of stars, and a planet sitting on top. Add this as a child node of Level: scrollingBackground false -1 false false 0 0 0 0 back2 false -1 false false 0 0 0 stars2 255 255 255 255 None 0 0 10 10 0 false back3 false -1 false false 0 0 0 stars3 255 255 255 255 None 0 0 100 100 0 false back4 false -1 false false 0 0 0 stars1 255 255 255 255 None 0 0 200 200 0 false 3 back5 false -1 false false 0 0 0 stars2 255 255 255 255 None 0 0 499.999969 499.999969 0 false 0.1 0.01 0.005 0.002 0.12 true planet1 false -1 false false 0 0 0 planet1 255 255 255 255 None 0 0 10 10 0 false 0.25 This looks complicated on first glance, but its really just a collection of sprites defining the background. Inside the "layers" tag, we simply have a list of sprite components with their proper assets defined (here, they are just stars). In the "Zooms" tag, we have a list of zoom factors for each layer. A smaller zoom factor means the object is further away, and a larger zoom factor means the object is closer. A zoom factor of 1.0 corresponds to exact per-pixel movement with the camera. A zoom factor of 0.5 corresponds to a layer moving half as fast as the camera, and a zoom factor of 1.5 exaggerates camera movements by 50 percent more than full speed (this is useful for putting graphical effects on top of the entire game.) Each of the layers is tiled so that it covers the whole visible area of the screen. You also may notice the "backgroundObjects" tag, which contains sprites that are not tiled. These are treated exactly like the layers, but they do not tile to fill the screen, but remain in a fixed position relative to the world. Scripting The meat of a level is defined by its script. The SpaceRTS engine has a hand-cobbled XML-based scripting engine, which I will give an overview of now. Overview The SpaceRTS scripting engine is based on linking together script primitives (called Events). An Event is simply defined as something which can be triggered, or not triggered. Different events trigger in different ways. For instance, the TimedEvent ''triggers after a certain amount of time has passed. The scripting engine simply loops through each event, and waits until the event has been triggered. In addition to the basic events, there are a bunch of meta-events that allow you to script logical sequences. In this way, you can form a branching tree of events. These are: *'ScriptSequence 'This event is just a list of other events, which are triggered in order. *'NegationEvent 'This event has a child event, and only triggers when the child event has not been triggered. *'AndEvent 'This event has two children, ''EventA and EventB. It triggers if and only if both of its children are triggered. *'OrEvent '''This event triggers if and only if one of its two children triggers. *'AutoTriggerEvent 'This event triggers automatically. Using these basic building blocks, you can create some extremely complex scripts. You should also know about some of the already-implemented events that you can use to make scripts for your level. *'ChangeMusicEvent 'Triggers automatically, and changes the background music of the game. *'DeathEvent 'Triggers only when a specific game component has died. *'ImageEvent 'Triggers automatically, and draws an image onto the screen, which will pan around (useful for cutscenes) *'LevelChangeEvent 'Triggers automatically, and tells the game whether or not the player has won or lost. *'PlayerSpawnRegion 'Triggers automatically, spawning the player's fleet in a region of the world. *'Region 'Triggers automatically, and defines a region of space that the game's AI can use. *'SoundEvent 'Triggers automatically, and plays a sound. *'SpawnEvent 'Spawns a specified game component in the world. Triggers automatically. *'SpawnRegion 'Spawns a list of game components randomly in a region. Triggers automatically. *'TeamDeathEvent 'Triggers only if all the units of a specified team have died. *'TextEvent C'reates a dialog text display, in which a character speaks to the player.When the text is done displaying, it triggers. *'WaypointEvent '''Creates a waypoint in space. If any of the player's units touch the waypoint, the event triggers. Creating A Script Let's start by making a simple script in our level. Add the following to the Level node: This is the base script of the level's scripting and events. It defines a sequence of other scripts. Let's go through each of the tags and what they represent. "scriptName" is just an identifier of the script. "runInBackground" tells the script manager whether or not this script should be run in parallel with other scripts. If this is set to true, then the script manager will not wait until the event has triggered to move onto the next one. "finished" is set to true when the script has triggered. If "skippable" is set to true, the player can press the space bar to trigger the script (this is useful for cut scenes and dialog). "backgroundEvents" is a list of events that will be run asynchronously with the rest of the events in the script sequence (here, backgroundEvents is empty) one example of a backgroundEvent you might want to put in a level is one which automatically cycles through different background music, or keeps track of a timer. "currentEvent" is an index into the event list defining which event the script sequence is currently executing. Now, let's add an event to our script, the player spawn event. Add the following to the "events" list. This defines a rectangle in space at position (-5,-5), with size (10,10). The player's fleet will be spawned in this region when the level starts.Now, let's also spawn a neutral shipyard next to the player. Right after the player has spawned, this will create a shipyard randomly in a region near the player. After that, let's add some exposition dialog so that the player knows what's going on: This defines a little dialog box that will pop up on the screen with scrolling text. Perhaps these events are a little too verbose, but they allow flexibility in how you display the text boxes, (their colors, positions, and avatar images). Now that we've made the radio guy inform the player of the situation, let's create a waypoint that the player should send his/her units to. Do this by making a waypoint event: This spawns a waypoint at position (65, -50), which will trigger when any ship of the Enforcer's team enters it. Once this has triggered, we give the player feedback by having the radio guy congratulate the player: Here, the radio guy is informing the player that he will spawn a probe for the player to destroy, so let's create a spawn region event to spawn the enemy probe in: This creates a probe of team "The Hive" somewhere in the region (50,-50, 50,50), as soon as the Radio Guy is done talking. Now, let's make the radio guy tell the player to destroy the probe. This will give the script manager time to spawn the probe. Finally, we just need to check to see if the probe is dead to see if the player has won. We'll also need to account for the possibility that the player has no units, in which case, the player will lose. We do this with an OrEvent: winSequence false false false This defines two branches: either the player destroys the probe, and the radio guy congratulates him/her and he/she wins, or the player somehow loses all of his/her units, and he/she loses. Now our level is pretty much complete. Let's add it into the game! Importing the Level Start by dragging TestLevel.xml into the SpaceRTSContent project, specifically into the folder called "Levels". Right click on the file once it has been imported into the project and select "Properties." Change the Content Importer property to "Component Importer." Change the Content Processor property to "SpaceRTSPipeline.ComponentProcessor". Now, the level will be an asset in the game. Campaign Next, we have to add the level to the default campaign of the game. This is just a list of levels that are executed in order. This is found in Levels/Campaign.xml. It should look like this: Levels/Level2 Levels/Level1 Just add this to the beginning of the level list: Levels/TestLevel This will make the test level you just created become the first level in the game, allowing you to test it! Category:Tutorials